#include "MyAsyncModule.h"

#include <stdexcept>

#include "utils/Logger.h"

namespace test_native_module {
MyAsyncModule::MyAsyncModule(std::shared_ptr<bridge::NativeToJSBridge> nativeToJsBridge,
                             std::shared_ptr<bridge::MessageQueue> nativeQueue)
    : nativeToJsBridge_(nativeToJsBridge), nativeQueue_(nativeQueue) {
  addFunc("testMax", std::bind(&MyAsyncModule::testMax, this, std::placeholders::_1, std::placeholders::_2),
          MethodType::ASYNC);
}

std::string MyAsyncModule::getName() { return "MyAsyncModule"; }

std::vector<MethodDescriptor> MyAsyncModule::getMethods() { return methodNames_; }

Dynamic MyAsyncModule::getConstants() {
  std::map<std::string, Dynamic> obj;
  obj["x"] = Dynamic((int64_t)1);
  obj["y"] = Dynamic(std::string("y"));
  return Dynamic(obj);
}

void MyAsyncModule::invokeSync(unsigned int methodId, std::vector<Dynamic>& args) {
  logError("sync unsupported in this module\n");
  abort();
}

void MyAsyncModule::invokeAsync(unsigned int methodId, std::vector<Dynamic>& args, unsigned int callbackId) {
  if (methodId >= methodNames_.size()) {
    throw std::runtime_error("method not found");
  }
  assert(methodNames_[methodId].type == MethodType::ASYNC);
  auto funcName = methodNames_[methodId].name;
  if (funcMap_.find(funcName) == funcMap_.end()) {
    throw std::runtime_error("method not found");
  }
  nativeQueue_->runOnQueue([this, callbackId, funcName, args] {
    try {
      auto val1 = args[0].getType() == Dynamic::Type::Double ? args[0].getDouble() : args[0].getInt64();
      auto val2 = args[1].getType() == Dynamic::Type::Double ? args[1].getDouble() : args[1].getInt64();
      funcMap_[funcName](val1, val2);
    } catch (std::exception e) {
      logError("Method" + funcName + " failed with error: " + e.what() + "/n");
    }
    nativeToJsBridge_->invokeCallback(callbackId);
  });
}

void MyAsyncModule::addFunc(std::string name, FunctionType func, MethodType funcType) {
  funcMap_[name] = func;
  methodNames_.push_back(MethodDescriptor(name, funcType));
}

Dynamic MyAsyncModule::testMax(int x, int y) {
  logInfo("testMax called with param: x: " + std::to_string(x) + ", y: " + std::to_string(y) + "\n");
  auto ret = x > y ? x : y;
  logInfo("testMax result is: " + std::to_string(ret) + "\n");
  return Dynamic();
}
}  // namespace test_native_module
